转载: 原文地址 zhuanlan.zhihu.com
@ObservedObject @Published 以及 ObservableObject 是 SwiftUI 常用的数据组织和监听方式。
一般写法如下,当 foo 发生 “改变” 时,将触发 MyView 的一次重新渲染。
class Foo: ObservableObject {
@Published var bar: String = 1
}
struct MyView: View {
@ObservedObject var foo: Foo
var body: some View {
//
}
}
class Foo: ObservableObject {
@Published var bar: String = 1
}
struct MyView: View {
@ObservedObject var foo: Foo
var body: some View {
//
}
}
但随着项目扩大,需要对数据进行分拆、整理,重新组织,可能需要放在多个 ObservableObject 中。在写 Vue 项目时,几乎都会使用 Vuex 来组织和管理数据,于是想在 SwiftUI 中也套用类似的数据组织方式,在项目扩大时,将 store 中的数据分拆成多个 module。
尝试在 SwiftUI 中实现,当代码运行时,会发现结果和预想并不相同。
class Foo: ObservableObject {
@Published var bar: Bar
}
class Bar: ObservableObject {
@Published var baz: String
}
struct MyView: View {
@ObservedObject var foo: Foo
var body: some View {
//
}
}
class Foo: ObservableObject {
@Published var bar: Bar
}
class Bar: ObservableObject {
@Published var baz: String
}
struct MyView: View {
@ObservedObject var foo: Foo
var body: some View {
//
}
}
当 baz 发生改变时,MyView 并没有刷新,那么是哪里出了问题呢?
class Foo: ObservableObject {
@Published var bar: Bar = Bar()
private var anyCancellable: AnyCancellable?
init() {
self.anyCancellable = self.objectWillChange.sink {
print("foo changed")
}
}
}
class Bar: ObservableObject {
@Published var baz: String = ""
private var anyCancellable: AnyCancellable?
init() {
self.anyCancellable = self.objectWillChange.sink {
print("bar changed")
}
}
}
struct MyView: View {
@ObservedObject var foo: Foo = Foo()
var body: some View {
Text("xxx")
}
}
class Foo: ObservableObject {
@Published var bar: Bar = Bar()
private var anyCancellable: AnyCancellable?
init() {
self.anyCancellable = self.objectWillChange.sink {
print("foo changed")
}
}
}
class Bar: ObservableObject {
@Published var baz: String = ""
private var anyCancellable: AnyCancellable?
init() {
self.anyCancellable = self.objectWillChange.sink {
print("bar changed")
}
}
}
struct MyView: View {
@ObservedObject var foo: Foo = Foo()
var body: some View {
Text("xxx")
}
}
通过日志会发现,当 baz 变化时打印了 "bar changed",而没有 "foo changed"。
- 首先,ObservableObject 提供了 objectWillChange 这个 Publisher 用于通知订阅者发生了变化,View 的刷新应该也依赖于此。
- 其次,Published 所提供的 Publisher 会触发 ObservableObject 所提供的 Publisher 发送更新消息。
因此,问题就在于,从 Bar 向外传递的更新消息 “断了”,Published 不太聪明,虽然懂得如何判断一个 String 是否发生了更新,但无法判断一个 ObservableObject 是否发生了变化。
因此就需要我们来 “助它一臂之力”。
class Foo: ObservableObject {
@Published var bar: Bar = Bar()
private var anyCancellable: AnyCancellable?
init() {
self.anyCancellable = self.bar.objectWillChange.sink {
self.objectWillChange.send()
}
}
}
class Bar: ObservableObject {
@Published var baz: String = ""
}
struct MyView: View {
@ObservedObject var foo: Foo = Foo()
var body: some View {
Text("xxx")
}
}
class Foo: ObservableObject {
@Published var bar: Bar = Bar()
private var anyCancellable: AnyCancellable?
init() {
self.anyCancellable = self.bar.objectWillChange.sink {
self.objectWillChange.send()
}
}
}
class Bar: ObservableObject {
@Published var baz: String = ""
}
struct MyView: View {
@ObservedObject var foo: Foo = Foo()
var body: some View {
Text("xxx")
}
}
当监听到 bar 发生了变化时,也触发 foo 的更新信息。
当有多个需要监听的 ObservableObject 时,可以使用不同的方法对消息管道进行整合,这部分和 Reactive 写法相似,有必要可以看看文档,或者了解一下 Reactive。